﻿#region

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Net;
using System.Security.Cryptography;
using MediaSage.Lib.Configuration;
using MediaSage.Lib.Database;
using NHibernate;
using NHibernate.Criterion;

#endregion

namespace MediaSage.Lib.Utilities
{
    public static class ImageUtils
    {
        #region Methods

        public static byte[] GenerateMD5HashFromImage(Image image)
        {
            var converter = new ImageConverter();
            try
            {
                var rawImageData = converter.ConvertTo(image, typeof(byte[])) as byte[];
                var md5 = new MD5CryptoServiceProvider();
                byte[] hash = md5.ComputeHash(rawImageData);
                return (hash);
            }
            catch (Exception)
            {
                return (null);
            }
        }

        public static Image GetImage(string URL)
        {
            if (URL == null || URL.Length == 0)
            {
                return (null);
            }
            Bitmap bitmap = null;
            try
            {
                var client = new WebClient();
                Stream stream = client.OpenRead(URL);
                bitmap = new Bitmap(stream);
                stream.Flush();
                stream.Close();
            }
            catch (Exception e)
            {
                return (null);
            }
            if (bitmap != null)
            {
                Image ret = bitmap;
            }
            return (bitmap);
        }

        public static EImageFormat GetImageFormat(Image image)
        {
            if (ImageFormat.Jpeg.Equals(image.RawFormat))
            {
                return EImageFormat.JPEG;
            }
            if (ImageFormat.Gif.Equals(image.RawFormat))
            {
                return EImageFormat.GIF;
            }
            if (ImageFormat.Png.Equals(image.RawFormat))
            {
                return EImageFormat.PNG;
            }
            if (ImageFormat.Bmp.Equals(image.RawFormat))
            {
                return EImageFormat.BMP;
            }
            if (ImageFormat.Wmf.Equals(image.RawFormat))
            {
                return EImageFormat.WMF;
            }
            if (ImageFormat.Tiff.Equals(image.RawFormat))
            {
                return EImageFormat.TIFF;
            }

            return EImageFormat.UNKNOWN;
        }

        public static ImageFormat GetImageFormatFromEnum(EImageFormat imageFormat)
        {
            switch (imageFormat)
            {
                case EImageFormat.BMP:
                    return ImageFormat.Bmp;
                case EImageFormat.GIF:
                    return ImageFormat.Gif;
                case EImageFormat.JPEG:
                    return ImageFormat.Jpeg;
                case EImageFormat.PNG:
                    return ImageFormat.Png;
                case EImageFormat.TIFF:
                    return ImageFormat.Tiff;
                case EImageFormat.WMF:
                    return ImageFormat.Wmf;
            }
            return ImageFormat.Png;
        }

        /// <summary>
        ///     Returns the image ID if an image with the same hash exists in the DB or returns a new Image ID if it does not
        /// </summary>
        /// <param name="image"> </param>
        /// <returns> Image ID of Image in the DB or new Image </returns>
        public static ImageEntity GetImageID(Image image, Config config, string filename)
        {
            Logger logger = config.Logger;
            logger.Log("Entering Utilities.GetImageID");

            ImageFormat saveToType = image.RawFormat;

            if (config.ConvertImages && (GetImageFormat(image) != config.ConvertImageToType))
            {
                switch (config.ConvertImageToType)
                {
                    case EImageFormat.BMP:
                        saveToType = ImageFormat.Bmp;
                        break;

                    case EImageFormat.GIF:
                        saveToType = ImageFormat.Gif;
                        break;

                    case EImageFormat.JPEG:
                        saveToType = ImageFormat.Jpeg;
                        break;

                    case EImageFormat.PNG:
                        saveToType = ImageFormat.Png;
                        break;

                    case EImageFormat.TIFF:
                        saveToType = ImageFormat.Tiff;
                        break;

                    case EImageFormat.WMF:
                        saveToType = ImageFormat.Wmf;
                        break;
                }

                logger.Log("Saving Image (" + filename + ") as " + config.ConvertImageToType.ToString());
                image.Save(filename, saveToType);
                image.Dispose();
                image = Image.FromFile(filename);
            }

            byte[] hash = GenerateMD5HashFromImage(image);
            logger.Log("ImageHash: " + hash);

            //TODO:
            // 1. Query the DB for an image with this hash, if it exists return that image object
            // 2. If it doesnt, create a new image object for this image
            // 3. Call Image.SaveImage(filename)
            // 4. Return the new image object
            ImageEntity dbImage = null;

            ISession session = DBUtil.GetSession();
            using (session.BeginTransaction())
            {
                dbImage =
                    session.CreateCriteria(typeof(ImageEntity)).Add(Restrictions.Eq("HashCode", hash)).UniqueResult
                        <ImageEntity>();
                if (dbImage != null)
                {
                    session.Close();
                    logger.Log("Image exists in DB already, existing Utilities.GetImageID with DB Object");
                    return dbImage;
                }
            }

            // This image doesnt exist, so lets create one Case 2 from above
            logger.Log("Image doesnt exist in database, creating new image");
            dbImage = new ImageEntity();
            dbImage.SaveImage(filename, config);
            return dbImage;
        }

        public static ImageEntity GetImageIDFromFile(string Filename, Config config)
        {
            Logger logger = config.Logger;
            logger.Log("Entering GetImageIDFromFile(" + Filename + ",configObject)");

            if (!File.Exists(Filename))
            {
                logger.Log(Logger.Levels.ERROR, "File: " + Filename + " not found or not readable");
                logger.Log("Exiting GetImageIDFromFile(" + Filename + ",configObject), rc==null");
                return null;
            }

            Image image;
            try
            {
                image = Image.FromFile(Filename);
            }
            catch (Exception e)
            {
                logger.Log(Logger.Levels.ERROR, "Exception reading image: " + Filename + ".  Exception: " + e.Message);
                logger.Log("Exiting GetImageIDFromFile(" + Filename + ",configObject), rc==null");
                return null;
            }

            if (image == null)
            {
                logger.Log("image==null, exiting GetImageIDFromFile(" + Filename + ",configObject), rc==null");
                return null;
            }
            ImageEntity dbImage = GetImageID(image, config, Filename);

            logger.Log("Exiting GetImageIDFromFile(" + Filename + ",configObject), rc==imageID");
            return (dbImage);
        }

        #endregion Methods
    }

    public static class Strings
    {
        #region Methods

        /// <summary>
        ///     Compute the distance between two strings.
        /// </summary>
        public static float ComputeStringDistance(string s, string t)
        {
            if (string.IsNullOrEmpty(s))
            {
                if (!string.IsNullOrEmpty(t))
                {
                    return t.Length;
                }
                return 0;
            }

            if (string.IsNullOrEmpty(t))
            {
                if (!string.IsNullOrEmpty(s))
                {
                    return s.Length;
                }
                return 0;
            }
            float cost;
            var d = new float[s.Length + 1, t.Length + 1];
            float min1;
            float min2;
            float min3;

            for (Int32 i = 0; i <= d.GetUpperBound(0); i += 1)
            {
                d[i, 0] = i;
            }

            for (Int32 i = 0; i <= d.GetUpperBound(1); i += 1)
            {
                d[0, i] = i;
            }

            for (Int32 i = 1; i <= d.GetUpperBound(0); i += 1)
            {
                for (Int32 j = 1; j <= d.GetUpperBound(1); j += 1)
                {
                    cost = Convert.ToInt32(!(s[i - 1] == t[j - 1]));
                    if (s[i - 1] != t[j - 1])
                    {
                        if (s[i - 1].ToString().ToUpper().Equals(t[j - 1].ToString().ToUpper()))
                        {
                            // Case is different, same char
                            cost = cost / 2;
                        }
                    }

                    min1 = d[i - 1, j] + 1;
                    min2 = d[i, j - 1] + 1;
                    min3 = d[i - 1, j - 1] + cost;
                    d[i, j] = Math.Min(Math.Min(min1, min2), min3);
                }
            }

            return d[d.GetUpperBound(0), d.GetUpperBound(1)];
        }

        #endregion Methods
    }

    public static class Files
    {
        public static void CopyFolder(string sourceFolder, string destFolder, bool overwrite)
        {
            if (!Directory.Exists(destFolder))
            {
                Directory.CreateDirectory(destFolder);
            }
            string[] files = Directory.GetFiles(sourceFolder);

            foreach (string file in files)
            {
                string name = Path.GetFileName(file);
                string dest = Path.Combine(destFolder, name);
                File.Copy(file, dest, overwrite);
            }
            string[] folders = Directory.GetDirectories(sourceFolder);
            foreach (string folder in folders)
            {
                string name = Path.GetFileName(folder);
                string dest = Path.Combine(destFolder, name);
                CopyFolder(folder, dest, overwrite);
            }
        }
    }

    public static class Util
    {
        #region Methods

        public static object CopyObject(object MergeObject, object Original, MediaMergeType mergeType)
        {
            Object b = Original;
            Object a = MergeObject;

            if (a == null && b == null)
            {
                return null;
            }

            if ((a != null && b != null) && (a.GetType() != b.GetType()))
            {
                throw new ArgumentException("Objects must be the same type");
            }
            Type objectType = null;

            if (a != null && a.GetType() != typeof(string) && a.GetType() != typeof(int) &&
                a.GetType() != typeof(Int16) && a.GetType() != typeof(long) && a.GetType() != typeof(DateTime) && a.GetType() != typeof(DateTime?) &&
                a.GetType() != typeof(List<string>) && a.GetType() != typeof(Bitmap) && a.GetType() != typeof(Guid) &&
                a.GetType() != typeof(TimeSpan) && a.GetType() != typeof(bool) && a.GetType() != typeof(bool?) &&
                a.GetType() != typeof(double) && a.GetType() != typeof(EMediaType) && a.GetType() != typeof(float)
                )
            {
                throw new ArgumentException(
                    "Objects must be of type: EMediaType, string, int, Int16, Long, DateTime, DateTime?, List<string>, Image, Guid, Bool, double, float or TimeSpan");
            }
            else
            {
                if (a != null)
                {
                    objectType = a.GetType();
                }
            }
            if ((objectType == null && a == null) &&
                (b.GetType() != typeof(string) && b.GetType() != typeof(int) && b.GetType() != typeof(Int16) &&
                 b.GetType() != typeof(long) && b.GetType() != typeof(DateTime) &&
                 b.GetType() != typeof(List<string>) && b.GetType() != typeof(Bitmap) && b.GetType() != typeof(Guid) &&
                 b.GetType() != typeof(TimeSpan) && b.GetType() != typeof(bool) && b.GetType() != typeof(bool?) &&
                 b.GetType() != typeof(double) && b.GetType() != typeof(EMediaType) && b.GetType() != typeof(float)))
            {
                throw new ArgumentException(
                    "Objects must be of type: EMediaType, string, int, Int16, Long, DateTime, List<string>, Image, Guid, Bool, double, float or TimeSpan");
            }
            else
            {
                if (objectType == null)
                {
                    if (b != null)
                    {
                        objectType = b.GetType();
                    }
                    else
                    {
                        // Both objects are null, we return null
                        return null;
                    }
                }
            }

            if (mergeType == MediaMergeType.OVERWRITE)
            {
                return a;
            }
            if (mergeType == MediaMergeType.WRITE_IF_LARGER)
            {
                if (objectType == typeof(long))
                {
                    if ((long)a > (long)b)
                    {
                        return a;
                    }
                }
                if (objectType == typeof(float))
                {
                    if ((float)a > (float)b)
                    {
                        return a;
                    }
                }

                if (objectType == typeof(EMediaType))
                {
                    if ((EMediaType)b == EMediaType.undef)
                    {
                        return a;
                    }
                }
                if (objectType == typeof(double))
                {
                    if ((double)a > (double)b)
                    {
                        return a;
                    }
                }

                if (objectType == typeof(bool))
                {
                    if ((bool)a || (bool)b)
                    {
                        return true;
                    }
                }
                if (objectType == typeof(bool?))
                {
                    if (b == null)
                    {
                        return a;
                    }
                    if ((bool)a || (bool)b)
                    {
                        return true;
                    }
                }
                if (objectType == typeof(Bitmap))
                {
                    if (b == null)
                    {
                        return a;
                    }
                    var a1 = (Bitmap)a;
                    var b1 = (Bitmap)b;
                    if ((a1.Size.Height * a1.Size.Width) > (b1.Size.Height * b1.Size.Width))
                    {
                        return a;
                    }
                }
                if (objectType == typeof(Guid))
                {
                    if (b == null || (Guid)b == Guid.Empty)
                    {
                        return a;
                    }
                }
                if (objectType == typeof(List<string>))
                {
                    var a1 = (List<string>)a;
                    var b1 = (List<string>)b;
                    if (a1.Count > b1.Count)
                    {
                        return a;
                    }
                }
                if (objectType == typeof(string))
                {
                    if (a.ToString().Length > b.ToString().Length)
                    {
                        return a;
                    }
                }
                if (objectType == typeof(int))
                {
                    if ((int)a > (int)b)
                    {
                        return a;
                    }
                }
                if (objectType == typeof(Int16))
                {
                    if ((Int16)a > (Int16)b)
                    {
                        return a;
                    }
                }

                if (objectType == typeof(DateTime))
                {
                    if (DateTime.Compare((DateTime)a, (DateTime)b) > 0)
                    {
                        return a;
                    }
                }
                if (objectType == typeof(TimeSpan))
                {
                    if (TimeSpan.Compare((TimeSpan)a, (TimeSpan)b) > 0)
                    {
                        return a;
                    }
                }
            }
            if (mergeType == MediaMergeType.WRITE_ONLY_ON_NULL_OR_EMPTY)
            {
                if (b == null)
                {
                    return a;
                }

                if (objectType == typeof(bool?) || objectType == typeof(bool))
                {
                    if ((bool?)a == null || (bool)a == false)
                    {
                        return b;
                    }
                    if ((bool)a || (bool)b)
                    {
                        return true;
                    }
                }

                if (objectType == typeof(long))
                {
                    if ((long)b == 0)
                    {
                        return a;
                    }
                }
                if (objectType == typeof(float))
                {
                    if ((float)b == 0)
                    {
                        return a;
                    }
                }

                if (objectType == typeof(EMediaType))
                {
                    if ((EMediaType)b == EMediaType.undef)
                    {
                        return a;
                    }
                }
                if (objectType == typeof(Bitmap))
                {
                    var a1 = (Bitmap)a;
                    var b1 = (Bitmap)b;

                    if (b == null || b1.Size.Height == 0 || b1.Size.Width == 0)
                    {
                        return a1;
                    }
                }

                if (objectType == typeof(Guid))
                {
                    if (b == null || (Guid)b == Guid.Empty)
                    {
                        return a;
                    }
                }

                if (objectType == typeof(List<string>))
                {
                    var b1 = (List<string>)b;
                    if (b1.Count == 0)
                    {
                        return a;
                    }
                }
                if (objectType == typeof(int))
                {
                    if ((int)b == 0)
                    {
                        return a;
                    }
                }
                if (objectType == typeof(Int16))
                {
                    if ((Int16)b == 0)
                    {
                        return a;
                    }
                }
                if (objectType == typeof(double))
                {
                    if ((Double)b == 0)
                    {
                        return a;
                    }
                }
                if (b.GetType() == typeof(string))
                {
                    if (String.IsNullOrEmpty((string)b))
                    {
                        return a;
                    }
                }
                if (b.GetType() == typeof(DateTime))
                {
                    if (DateTime.Compare((DateTime)b, (new DateTime())) == 0)
                    {
                        return a;
                    }
                }
                if (b.GetType() == typeof(TimeSpan))
                {
                    if (TimeSpan.Compare((TimeSpan)b, (new TimeSpan())) == 0)
                    {
                        return a;
                    }
                }
            }

            if (mergeType == MediaMergeType.COPY_NEW)
            {
                if (a == null)
                {
                    return b;
                }
                if (b == null)
                {
                    return a;
                }
                if (objectType == typeof(long))
                {
                    if ((long)b == 0)
                    {
                        return a;
                    }
                }
                if (objectType == typeof(int))
                {
                    if ((int)b == 0)
                    {
                        return a;
                    }
                }
                if (objectType == typeof(Int16))
                {
                    if ((Int16)b == 0)
                    {
                        return a;
                    }
                }
                if (objectType == typeof(double))
                {
                    if ((double)b == 0)
                    {
                        return a;
                    }
                }
                if (objectType == typeof(float))
                {
                    if ((float)b == 0)
                    {
                        return a;
                    }
                }
                if (objectType == typeof(DateTime))
                {
                    if ((DateTime)a > (DateTime)b)
                    {
                        return a;
                    }
                    return b;
                }
                if (objectType == typeof(TimeSpan))
                {
                    if ((TimeSpan)b == TimeSpan.Zero)
                    {
                        return a;
                    }

                    return a;
                }

                if (objectType == typeof(string))
                {
                    if (b.ToString().Length == 0)
                    {
                        return a;
                    }
                    return b;
                }
                if (objectType == typeof(EMediaType))
                {
                    if ((EMediaType)b == EMediaType.undef || b == null)
                    {
                        return a;
                    }
                    return b;
                }
                if (objectType == typeof(Image))
                {
                    var c = (Image)b;
                    if (c.Height * c.Width != 0)
                    {
                        return a;
                    }
                    return b;
                }
                if (objectType == typeof(Guid))
                {
                    if ((Guid)b == Guid.Empty)
                    {
                        return a;
                    }
                    return b;
                }
                if (objectType == typeof(bool))
                {
                    return a;
                }
            }

            if (objectType == typeof(List<string>))
            {
                if (a == null)
                {
                    return b;
                }
                if (b == null)
                {
                    return a;
                }

                var a1 = new List<string>();
                var a2 = (List<string>)a;
                foreach (string str in a2)
                {
                    a1.Add(str);
                }
                var b1 = new List<string>();
                var b2 = (List<string>)b;
                foreach (string str in b2)
                {
                    b1.Add(str);
                }
                foreach (string str in a1)
                {
                    if (!b1.Contains(str))
                    {
                        b1.Add(str);
                    }
                }
                return b1;
            }
            return b;
        }

        #endregion Methods
    }
}